﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;

/**
 * Socket建立流程
 * 1.Socket
 * 2.Bind
 * 3.Listen
 * 4.BeginAccept
 * */
namespace SocketServer
{
    public class Serv
    {
        //监听嵌套字
        public Socket listenfd;
        //客户端连接
        public Conn[] conns;
        //最大连接数
        public int maxConn = 50;

        //获取连接池索引,返回负数表示失败
        public int NewIndex() {
            if (conns == null)
                return -1;
            for (int i = 0; i < conns.Length; i++) {
                if (conns[i] == null)
                {
                    conns[i] = new Conn();
                    return i;
                }
                else if (conns[i].isUse == false) {
                    return i;
                }
            }
            return -1;
        }

        //开启服务器
        public void Start(string host, int port) {
            //连接池
            conns = new Conn[maxConn];
            for (int i = 0; i < maxConn; i++) {
                conns[i] = new Conn();
            }

            //Socket
            listenfd = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //Bind
            IPAddress ipAr = IPAddress.Parse(host);
            IPEndPoint ipEp = new IPEndPoint(ipAr, port);
            listenfd.Bind(ipEp);
            //Listen 挂起连接队列的最大长度。
            listenfd.Listen(maxConn);
            //Accept
            listenfd.BeginAccept(AcceptCb, null);
            Console.WriteLine("[服务器]启动成功");
        }

        //Accept回调
        /// <summary>
        /// 处理三件事情 1.给新的连接分配conn 2.异步接受客户端数据 3.再次调用BeginAccept实现循环
        /// </summary>
        /// <param name="ar"></param>
        public void AcceptCb(IAsyncResult ar) {
            try
            {
                Socket socket = listenfd.EndAccept(ar);
                int index = NewIndex();

                if (index < 0)
                {
                    socket.Close();
                    Console.WriteLine("[警告]连接已满");
                }
                else
                {
                    //给新的连接分配Conn
                    Conn conn = conns[index];
                    conn.Init(socket);
                    string adr = conn.GetAdress();
                    Console.WriteLine("客户端连接[" + adr + "] conn池 ID： " + index);
                    //异步接收客户端数据
                    conn.socket.BeginReceive(conn.readBuff, conn.buffCount, conn.BuffRemain(), SocketFlags.None, ReceiveCb, conn);
                }
                //再次调用BeginAccept实现循环
                listenfd.BeginAccept(AcceptCb, null);
            }
            catch (Exception e) {
                Console.WriteLine("AcceptCb失败"+e.Message);
            }
        }

        //接收CB
        private void ReceiveCb(IAsyncResult ar) {
            Conn conn = (Conn)ar.AsyncState;
            try
            {
                int count = conn.socket.EndReceive(ar);
                //关闭信号
                if (count <= 0)
                {
                    Console.WriteLine("收到[" + conn.GetAdress() + "] 断开连接");
                    conn.Close();
                    return;
                }

                //数据处理
                //string str = System.Text.Encoding.UTF8.GetString(conn.readBuff, 0, count);
                //Console.WriteLine("收到[" + conn.GetAdress() + "] 数据 " + str);
                ////str = conn.GetAdress() + ":" + str;
                //byte[] bytes = System.Text.Encoding.Default.GetBytes(str);
                //显示字节流
                string showStr = "";
                for (int i = 0; i < count; i++)
                {
                    int b = (int)conn.readBuff[i];
                    showStr += b.ToString() + " ";
                }
                Console.WriteLine("[服务器接收]字节流：" + showStr);
                //解析协议
                Int16 messageLen = BitConverter.ToInt16(conn.readBuff, 0);
                Int16 protocal = BitConverter.ToInt16(conn.readBuff, 2);
                Int16 protocalType = BitConverter.ToInt16(conn.readBuff, 3);
                Int16 strLen = BitConverter.ToInt16(conn.readBuff, 5);
                string str = System.Text.Encoding.UTF8.GetString(conn.readBuff, 7, strLen);
                Console.WriteLine("[服务器接收] 长度：" + messageLen);
                Console.WriteLine("[服务器接收] 协议号：" + protocal);
                Console.WriteLine("[服务器接收] 协议类型：" + protocalType);
                Console.WriteLine("[服务器接收] 字符串：" + str);
                //Send（echo）
                byte[] bytes = new byte[count];
                Array.Copy(conn.readBuff, bytes, count);

                //广播  给conn池子中每个客户端发送消息
                for (int i = 0; i < conns.Length; i++)
                {
                    if (conns[i] == null)
                        continue;
                    if (!conns[i].isUse)
                        continue;
                    Console.WriteLine("将消息转播给" + conns[i].GetAdress());
                    conns[i].socket.Send(bytes);
                }

                //继续接收
                conn.socket.BeginReceive(conn.readBuff, conn.buffCount, conn.BuffRemain(), SocketFlags.None, ReceiveCb, conn);

            }
            catch (Exception e) {
                Console.WriteLine("收到["+conn.GetAdress()+"] 断开连接");
                conn.Close();
            }
        }








    }
}
